# coding=utf-8
"""
author = jamon
"""
from __future__ import annotations
import asyncio

from websockets.exceptions import ConnectionClosed
from typing import (
    Any,
    Awaitable,
    Callable,
    TYPE_CHECKING,
    Type)

from obespoir.base.ob_route import ObRoute
from obespoir.share.espoir_log import global_log
from obespoir.share.singleton import Singleton
from obespoir.base.ob_protocol import DataException
from obespoir.base.global_object import GlobalObject

if TYPE_CHECKING:
    from obespoir.websocketserver.protocol import WebSocketProtocol


class WebSocketRoute(ObRoute):

    def get_target(self, target_key):
        """Get a target from the service by name."""
        self._lock.acquire()
        try:
            target = self._targets.get(target_key, None)
            if not target:
                target = self._targets.get(0, None)
        finally:
            self._lock.release()
        return target


websocket_route = WebSocketRoute()


def webSocketRouteHandle(target):
    websocket_route.map_target(target)


class WebsocketController(object, metaclass=Singleton):

    def __init__(self):
        pass

    async def msg_handler(          # noqa
            self,
            websocket: Callable[[WebSocketProtocol, str], Awaitable[Any]],
            path: str):
        global_log.debug("start", address=websocket.remote_address, path=path)
        while True:
            try:
                data = await asyncio.wait_for(
                    websocket.recv(), timeout=GlobalObject().ws_timeout)
                global_log.debug("received", data=data)
                # await websocket.send("hello")
                while data:  # 解决TCP粘包问题
                    data = await websocket.process_data(data)
            except asyncio.TimeoutError:
                global_log.info("timeout", address=websocket.remote_address)
                await websocket.close()
            except ConnectionClosed as e:
                global_log.info("connection lose", address=websocket.remote_address,
                                error_code=e.code, reason=e.reason)
                return 0
            except DataException:
                global_log.info("data decrypt error", address=websocket.remote_address)
                await websocket.close()
                return 0

    async def after_open_handler(self, ws: Type[WebSocketProtocol]):
        """连接建立后执行的特殊逻辑, 如可以做客户端热更"""
        pass

    async def after_close_handler(self, ws: Type[WebSocketProtocol]):
        """连接断开时执行的特殊逻辑，如部分缓存的清理"""
        pass
